import cv2
import numpy as np

dsize = (55, 88)  # 统一尺度

# 展示图像，封装成函数
def cv_show_image(name, img):
    cv2.imshow(name, img)
    #cv2.waitKey(0)  # 等待时间，单位是毫秒，0代表任意键终止
    #cv2.destroyAllWindows()


def contours_sort(contours, method=0):
    # x, y, w, h = cv2.boundingRect(contour) 得到某个轮廓的xy坐标和长度和宽度
    if method == 0:
        # 升序，根据 每个轮廓的 外界矩形的 x 坐标位置排序
        contours = sorted(contours, key=lambda x: cv2.boundingRect(x)[0])
    else:
        # 升序，根据 每个轮廓的 外界矩形的 x 坐标位置排序
        contours = sorted(contours, key=lambda x: cv2.boundingRect(x)[0], reverse=True)
    return contours


def resize(image, width=None, height=None, inter=cv2.INTER_AREA):
    h, w = image.shape[:2]
    if width is None and height is None:
        return image
    if width is None:
        r = height / float(h)
        dim = (int(w * r), height)
    else:
        r = width / w
        dim = (width, int(r * h))
    resized = cv2.resize(image, dim, interpolation=inter)
    return resized


# ===========================================================
# ================== 第一部分: 加载模板图片，得到其中的关于数字模板信息
# ===========================================================

template = cv2.imread('./res/number-tempture0.png')

# 进行灰度值和二值化转换
template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
# cv_show_image('template_gray', template_gray)

# 形成二值图像，因为要做轮廓检测，行成黑底白字
ret, template_thresh = cv2.threshold(template_gray, 127, 255, cv2.THRESH_BINARY_INV)
# cv_show_image('template_thresh', template_thresh)

# 进行轮廓提取，得到数字的信息，RETR_EXTERNAL 就是只是需要外轮廓。
template_contours, hierarchy = cv2.findContours(template_thresh,
                                                cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 将轮廓排序，位置从小到大就是数字的信息
template_contours = contours_sort(template_contours)

# 遍历模板，使用cv2.boudingRect获得轮廓的位置，提取位置对应的图片，与数字结合构造成模板字典
dict_template = {}
for i, contour in enumerate(template_contours):
    # 画出其外接矩阵，获得其位置信息
    x, y, w, h = cv2.boundingRect(contour)
    template_img = template_thresh[y:y + h, x:x + w]
    # 使用cv2.resize变化模板的大小
    template_img = cv2.resize(template_img, dsize)
    # cv_show_image('template_img{}'.format(i), template_img)
    dict_template[i] = template_img  # 获得当前图像的位置和ROI区域，并且保存了下来这个区域

# ===========================================================
# ================== 第二部分: 加载目标需要检测的银行卡
# ===========================================================

bankcard = cv2.imread('./res/back_card.png')
bankcard_h, bankcard_w, bankcard_c = bankcard.shape  # 得到目标图像的长宽信息

# 进行灰度值和二值化转换
bankcard_gray = cv2.cvtColor(bankcard, cv2.COLOR_BGR2GRAY)
# cv_show_image('bankcard_gray', bankcard_gray)

# 形成二值图像，因为要做轮廓检测，行成黑底白字
ret, bankcard_thresh = cv2.threshold(bankcard_gray, 127, 255, cv2.THRESH_BINARY)
# cv_show_image('bankcard_thresh', bankcard_thresh)

# 目标图像很明显有一些小毛刺，因此我们可以做个开运算来取消毛刺儿。
kernel = np.ones((3, 3), np.uint8)
bankcard_open = cv2.morphologyEx(bankcard_thresh, cv2.MORPH_OPEN, kernel, iterations=1)
cv_show_image('bankcard_open', bankcard_open)

# 使用cv2.findContours进行轮廓的识别，还是只取外轮廓。
bankcard_contours, hierarchy = cv2.findContours(bankcard_open, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
banck_card_cnts = []
draw_img = bankcard.copy()  # 阶段性测试查看使用
# 循环轮廓，将在某个区域的contours加入
for i, contour in enumerate(bankcard_contours):
    x, y, w, h = cv2.boundingRect(contour)
    # 数字的x 坐标在 一定的位置范围
    if 0.5 * bankcard_h < y < 0.6 * bankcard_h:  # 目测的，数字在这个该范围内
        banck_card_cnts.append((x, y, w, h))
        draw_img = cv2.rectangle(draw_img, pt1=(x, y), pt2=(x + w, y + h), color=(0, 0, 255),
                                 thickness=2)  # 画出这个矩形，会在原图上画
cv_show_image('rectangle_contours_img', draw_img)
del draw_img

# ===========================================================
# ================== 第三部分: 进行目标匹配并画出来
# ===========================================================

draw_img = bankcard.copy()  # 阶段性测试查看使用
for i, locs in enumerate(banck_card_cnts):

    x, y, w, h = locs[:]  # 保留了在原始图像的位置信息
    dst_img = bankcard_thresh[y:y + h, x:x + w]  # 获得当前图像的位置和ROI区域
    dst_img = cv2.resize(dst_img, dsize)  # 进行resize和模板大小一样，统一size，方便匹配
    # cv_show_image('rectangle_contours_img', dst_img)

    tm_vals = {}
    for number, temp_img in dict_template.items():
        # 模板匹配，采用计算相关性系数，值越大越相关，带有归一化
        res = cv2.matchTemplate(dst_img, temp_img, cv2.TM_CCOEFF_NORMED)
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
        tm_vals[number] = max_val

    # 计算出哪个更加匹配，采用计算相关性系数，值越大越相关，因此找出最大的那个数值的key
    number_tm = max(tm_vals, key=tm_vals.get)

    # 在图像上画出结果来
    draw_img = cv2.rectangle(draw_img, pt1=(x, y), pt2=(x + w, y + h), color=(0, 0, 255),
                             thickness=2)  # 画出这个矩形，会在原图上画
    cv2.putText(draw_img, str(number_tm), (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.65,
                color=(0, 0, 255), thickness=2)

cv_show_image('final_result', draw_img)



cv2.waitKey(0)  # 等待时间，单位是毫秒，0代表任意键终止
cv2.destroyAllWindows()

